package com.captjack.distributedlock.aspect;

import com.captjack.distributedlock.annotation.DistributedLockByRedis;
import com.captjack.distributedlock.lock.RedisDistributedLock;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

import java.util.UUID;

/**
 * 开启redis分布式锁切面
 *
 * @author Capt Jack
 * @date 2018/7/12
 */
@Configuration
@Aspect
@EnableAspectJAutoProxy(proxyTargetClass = true)
@ConditionalOnProperty(value = "distributed.lock.redis.enable", havingValue = "true")
public class DistributedLockByRedisAspect extends BaseDistributedLock {

    /**
     * 分布式锁前缀
     */
    private static final String DISTRIBUTED_LOCK_ID_PREFIX = "captjack-distribute-lock-";

    /**
     * 日志
     */
    private static final Logger logger = LoggerFactory.getLogger(DistributedLockByRedisAspect.class);

    /**
     * 分布式锁操作
     */
    private final RedisDistributedLock redisDistributedLock;

    /**
     * @param proceedingJoinPoint 切点信息
     * @return 执行信息
     */
    @Around(value = "@annotation(com.captjack.distributedlock.annotation.DistributedLockByRedis)")
    Object service(ProceedingJoinPoint proceedingJoinPoint) {
        Object result = null;
        // 是否获得锁
        boolean isLock = false;
        // 锁id，请求id
        String lockId = null, requestId = null;
        // 注解实例
        DistributedLockByRedis distributedLockByRedis;
        try {
            // 获取注解实例
            distributedLockByRedis = super.getAnnotation(proceedingJoinPoint, DistributedLockByRedis.class);
            // 获取锁id，请求id（使用UUID）
            lockId = DISTRIBUTED_LOCK_ID_PREFIX + distributedLockByRedis.lockId();
            requestId = UUID.randomUUID().toString();
            // 获取锁
            isLock = redisDistributedLock.acquire(lockId, requestId, distributedLockByRedis.lockExpireTime(), distributedLockByRedis.lockExpireTimeUnit(), distributedLockByRedis.isExpireLock());
            // 获取锁成功，执行操作，解锁标识设置为false
            if (isLock) {
                result = proceedingJoinPoint.proceed();
            }
        } catch (Throwable throwable) {
            logger.error("DistributedLockByRedisAspect execute error! ", throwable);
        } finally {
            // 总是在最后释放锁
            if (isLock) {
                try {
                    do {
                        isLock = !redisDistributedLock.release(lockId, requestId);
                    } while (isLock);
                } catch (Exception e) {
                    logger.error("DistributedLockByRedisAspect release lock error! ", e);
                }
            }
        }
        // 返回结果
        return result;
    }

    @Autowired
    public DistributedLockByRedisAspect(@Qualifier(value = "redis_distributed_lock") RedisDistributedLock redisDistributedLock) {
        this.redisDistributedLock = redisDistributedLock;
    }

}
